package client;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.Socket;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Random;
import java.util.StringTokenizer;

import javax.swing.Timer;

/**
 * 
 */

/**
 * @author Sunny
 *
 */
public class Server extends Thread{

	public static final String ip = "localhost";
	public static final int port = 3333;
	
	public static final int startingTime = 10000;
	
	public static final String joinCmd = "startGame";
	public static final String moveCmd = "move";
	public static final String quitCmd = "quit";
	
	public static final String OKCode = "200";
	
	private static Maze game = null;
	private static ArrayList<Player> pList = new ArrayList<Player>();
	
	/**
	 * Constructor
	 */
	public Server()
	{
		//start the connection to wait for the incoming messages
		try {
			ServerConnection.openListeningConnection(port);			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		//create Maze game object, isPlaying is still false - game is not yet started
		game = new Maze(3,10);		
		System.out.println("Server created!");
	}
	
	public Maze getGame()
	{
		return game;
	}
	
	/**
	 * Start game
	 */
	private boolean startGame()
	{
		if (game == null)
			return false;
		
		if (game.getIsPlaying())
			return false;
		
		//server should have finalized player list
		game.setPlayers(pList);
		allocPlayersPos();
		game.setIsPlaying(true); //important
		//		
		return true;
	}
	
	
	public void run()
	{
		System.out.println("Server is running...");
		boolean firstPlayer = true;
		
		//
		while(true)
		{
			String  msg = ServerConnection.receive();
			System.out.println("Message received: "+msg);
			
			if (msg.startsWith("startGame") && firstPlayer)
			{
				firstPlayer = false;
				
				Runnable r = new Runnable() {
				  public void run(){
				    try {
				    	Thread.sleep(startingTime);
				    	startGame();	
				    	broadcastGameMsg();
						System.out.println("Start the Game");					    
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				  }
				};
				Thread T = new Thread(r);
				T.start();
			}
			
			if (!game.getIsPlaying() && !firstPlayer)
			{
				broadcastGameMsg();
				firstPlayer = true;
			}
			
			new ServerSubThread(ServerConnection.connectionSocket,msg).start();
			//ServerConnection.closeConnection();
		}
		/*
		//start waiting for the incoming messages
		while(true)
		{
			try {
				String msg = Connection.receive(port);

				if (msg.startsWith("startGame"))
				{
					String ip = Connection.connectionSocket.getInetAddress().toString().substring(1);
					int port = Connection.connectionSocket.getPort();

					//System.out.println(port);
					
					Player p = createPlayer(ip,port);
					startGame();
					if (firstPlayer)
					{
						Runnable r = new Runnable() {
							  public void run(){
							    try {
							    	Thread.sleep(20000);
							    	game.setIsPlaying(true);
									//System.out.println("Start Game");
							    	broadcastStartGame();
								} catch (Exception e) {
									// TODO Auto-generated catch block
									e.printStackTrace();
								}
							  }
							};
						Thread T = new Thread(r);
						T.start();
						firstPlayer = false;
					}
				}
				else if (msg.startsWith("move"))
				{
					StringTokenizer st = new StringTokenizer(msg,"_");
					String id = null;
					String dir = null;
					Player p = null;
					
					if (st.hasMoreElements())
						st.nextElement();
					
					if (st.hasMoreElements())
					{
						id = st.nextToken();
						
						for (int i=0;i<pList.size();i++)
						{
							Player pl = pList.get(i);
							if (pl.getIdNo().equalsIgnoreCase(id))
							{
								p = pl;
								break;
							}
						}						
					}
					
					if (st.hasMoreElements())
					{
						dir = st.nextToken();
					}
					
					//notify player thread
					p.setDir(dir);
					p.setGame(game);
					Thread t = new Thread(p);
					t.start();					
				}
				
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		*/
	}
	
	/**
	 * Randomly allocate players into the maze with N*N grid
	 */
	private boolean allocPlayersPos()
	{
		Random rand = new Random();
		
		//Maximum number of players allowed is maze's length
		if (pList.size() > game.getLength())
			return false;
		
		for (int x=0; x < pList.size(); x++)
		{
			Player p = pList.get(x);
			int y = rand.nextInt(game.getLength());
			int numOfTreasures = game.getTreasureLocVal(x, y);
			
			p.setXPos(x);			
			p.setYPos(y);
			p.addTreaCollected(numOfTreasures);
			game.setTreasureLocVal(x, y, 0);
			game.setTreaAvailable(game.getTreaAvailable()-numOfTreasures);
			
			pList.set(x, p);
		}
		
		return true;
	}
	
	private void broadcastGameMsg()
	{
		System.out.println("Broadcasting....size="+pList.size());
		for (int x=0; x < pList.size(); x++)
		{
			Player p = pList.get(x);
			String msg = OKCode+"_"+p.getIdNo()+"_"+p.getXPos()+"_"+p.getYPos()+"_"+p.getTreaCollected()
			+"_"+game.gameState();
			
			ServerConnection.send(msg,p.getSocket());
		}		
	}
	
	/**
	 * 
	 */
	
	/**
	 * Set methods
	 */
	
	/**
	 * Get methods
	 */
	
	/**
	 * Server Thread
	 */
	 static class ServerSubThread extends Thread {
		 
		 Socket connectionSocket;
		 String msg = null;
		 
		 public ServerSubThread(Socket soc, String clientMsg) {
				// TODO Auto-generated constructor stub
			msg = clientMsg;
			connectionSocket = soc;
		 }
		 
	     public void run() {
	    	  String ip = connectionSocket.getInetAddress().toString().substring(1);
	  		  int port = connectionSocket.getPort();
	    	  
	  		  if (msg.startsWith(joinCmd) && !game.getIsPlaying())
	  		  {	  		  
		          synchronized (pList) {
		            /********** Critical Section *******************/
		        	 createPlayer(ip,port);
		            /********** Critical Section *******************/
		          }
	  		  }
	  		  else if (msg.startsWith(joinCmd) && game.getIsPlaying())
	  		  {
	  			  String msg = "Game is started! Please try again later";
	  			  ServerConnection.send(msg,connectionSocket);
	  		  }
	  		  else if (msg.startsWith(moveCmd))
	  		  {
		  		   	StringTokenizer st = new StringTokenizer(msg,"_");
					String id = null;
					String dir = null;
					Player p = null;
					
					//move command
					if (st.hasMoreElements())
						st.nextElement();
					
					//userID
					if (st.hasMoreElements())
					{
						id = st.nextToken();
						
						//lookup for the player
						for (int i=0;i<pList.size();i++)
						{
							Player pl = pList.get(i);
							if (pl.getIdNo().equalsIgnoreCase(id))
							{
								p = pl;
								break;
							}
						}						
					}
					
					//move direction
					if (st.hasMoreElements())
					{
						dir = st.nextToken();
					}
					
					synchronized (game) {
			            /********** Critical Section *******************/
			        	boolean ret = game.movePlayer(dir, 1, p);
			            /********** Critical Section *******************/			     
			        }
					
					//send msg to the client
					String msg = OKCode+"_"+p.getIdNo()+"_"+p.getXPos()+"_"+p.getYPos()+"_"+p.getTreaCollected()+"_"
					+"_"+game.gameState();					
					ServerConnection.send(msg,connectionSocket);
	  		  }
	  		  else if (msg.startsWith(quitCmd))
	  		  {
	  			  StringTokenizer st = new StringTokenizer(msg,"_");
	  			  Player p = null;
	  			  String id = null;
	  			  
	  			  //quit command
	  			  if (st.hasMoreElements())
	  				  st.nextElement();
					
	  			  //userID
	  			  if (st.hasMoreElements())
	  			  {
	  				  id = st.nextToken();
						
						//lookup for the player
						for (int i=0;i<pList.size();i++)
						{
							Player pl = pList.get(i);
							if (pl.getIdNo().equalsIgnoreCase(id))
							{
								p = pl;
								break;
							}
						}						
	  			  }
	  			  
	  			  synchronized (game) {
			            /********** Critical Section *******************/
			        	boolean ret = game.quitGame(p);
			            /********** Critical Section *******************/			     
			      }
	  			  
	  			  //send msg to the client
	  			  String msg = OKCode+"_Bye";					
	  			  ServerConnection.send(msg,connectionSocket);
	  			
	  		  }
	      }
	      
	      private Player createPlayer(String clientIP, int clientPortNo)
		  {
	    	  //check if the maximum number of players hit
		  	  if (pList.size() == game.getLength())
		  		  return null;
		  		
		  	  SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
		  	  String tt = dateFormat.format(new Timestamp(System.currentTimeMillis()));
		  		
		  	  Player p = new Player(clientIP,clientPortNo,tt,connectionSocket);
		  	  pList.add(p); 
		  	  System.out.println("a player is created!");
		  		
		  	  return p;
		  }
	      
	    
	   }
	

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.out.println("Main method");
		Server server = new Server();
		server.run();
	}
}
